<?php

namespace Yjius\common;

/**
 * 调试日志操作类
 */
class DebugLog
{
    private $timeList = null;
    private $infoList = null;

    private static $instance = false;
    private static $stop = false; // 表示是否停止记录日志

    private function __construct()
    {
    }

    private function __clone()
    {
    }

    /**
     * 初始化调试日志操作类，没有经过初始化的后续调试代码都不会生效
     */
    public static function init()
    {
        if (!self::$instance) {
            self::$instance = new DebugLog();
        }
        return self::$instance;
    }

    /**
     * 记录运行时的业务日志信息
     *
     * @param $label string     一个简单描述，方便日志查看
     * @param $info  array      记录的信息
     * @param $uid   string     用户uid
     * @param array $extra
     */
    public static function info($label, $info, $uid = '', $extra = [])
    {
        if (self::$stop) {
            return;
        }
        if (self::$instance === false) {
            return;
        }
        // 请求uuid，请求唯一标识，可以在业务模块的入口基类生成一个请求唯一表示，这样能将一系列请求串起来
        $requestUuid = '';
        if (defined('REQUEST_UUID')) {
            $requestUuid = REQUEST_UUID;
        }
        // 请求路径
        $info = [
            'requestUuid' => $requestUuid,
            'label' => $label,
            '_url' => !empty($_REQUEST['_url']) ? $_REQUEST['_url'] : "unknown",
            '_uri' => !empty($_SERVER['REQUEST_URI']) ? htmlspecialchars($_SERVER['REQUEST_URI']) : "unknown",
            'info' => json_encode($info),
            'uid' => $uid,
            'addTime' => microtime(true),
        ];
        $info = array_merge($info, $extra);
        self::$instance->infoList[] = $info;
    }

    /**
     * 记录时间，方便调试程序执行逻辑和每一步的执行效率
     */
    public static function time($label)
    {
        if (self::$stop) {
            return;
        }
        if (self::$instance === false) {
            return;
        }
        self::$instance->timeList[] = array($label, microtime(true));
    }

    /**
     * 输出日志
     */
    public static function show($getHtml = 0)
    {
        if (self::$instance === false) {
            return false;
        }
        if (!empty($getHtml)) {
            return self::$instance->_showViews();
        } else {
            echo self::$instance->_showViews();
            exit;
        }
    }

    /**
     * Purpose: 保存日志
     */
    public static function saveLog()
    {
        if (self::$instance === false) {
            return;
        }
        self::$instance->_saveInfo();
    }

    /**
     * @desc 清除log数据
     */
    public static function cleanLogs()
    {
        self::$instance->infoList = [];
        self::$instance->timeList = [];
    }

    /**
     * 停止记录日志
     */
    public static function stopLog()
    {
        self::$stop = true;
    }

    /**
     * 重新记录日志
     */
    public static function restartLog()
    {
        self::$stop = false;
    }

    /**
     * 获取所有记录的时间
     */
    private static function _getTime()
    {
        if (self::$instance === false) {
            return [];
        }
        return self::$instance->timeList;
    }

    /**
     * 将日志信息写入redis队列中
     */
    private function _saveInfo()
    {
        if (!empty($this->infoList)) {
            $len = count($this->infoList);
            if ($len > 1) { // 如果记录的信息超过两条，计算一下消耗时间，并放到最后一条记录中
                $this->infoList[$len - 1]['costTime'] = $this->infoList[$len - 1]['addTime'] - $this->infoList[0]['addTime'];
            }

            $str = json_encode($this->infoList);
            $str = str_replace("\n", ' ', $str);
            $str .= "\n";
            $debug_log_path = getenv('DEBUG_LOG_PATH') ?: dirname(dirname(__DIR__)) . '/examples/runtime/log';
            $file_path = $debug_log_path . '/app_debug.log';
            if ($fd = @fopen($file_path, 'a')) {
                fputs($fd, $str);
                fclose($fd);
            }
        }
    }


    /**
     * 将microtime的时间字符串转换为float型的毫秒时间
     */
    private function _floatMicrotime($mt)
    {
        if (strpos($mt, ' ')) {
            list($ms, $m) = explode(' ', $mt);
            return ($m + $ms) * 1000;
        } else {
            return floatval($mt) * 1000;
        }
    }

    /**
     * 计算两个microtime时间的间隔时间
     * @param $m1 开始时间
     * @param $m2 结束时间
     * @param int|保留小数位 $round 保留小数位
     * @return float
     */
    private function _intervalTime($m1, $m2, $round = 3)
    {
        return round(($this->_floatMicrotime($m2) - $this->_floatMicrotime($m1)), $round);
    }

    /**
     * 将调试信息生成可视化的HTML代码
     */
    private function _showViews()
    {
        $showTime = microtime();
        $output = array();
        $output[] = "<script src=\"https://www.jq22.com/jquery/jquery-1.10.2.js\"></script>";
        $output[] = "\n";
        $output[] = "<style type=\"text/css\">
*{margin:0; padding:0;}
body{ background:#fff;}
.clearfix:after{ display:block; visibility:hidden; clear:both; height:0; content:\"\";}
.clearfix{zoom:1;}

h1{ text-align: center; font-family: 'Microsoft YaHei'; font-size: 24px; color: #333; padding:20px 0; border-bottom: 1px solid #ddd;}
h2{ text-align: center; font-family: 'Microsoft YaHei'; font-size: 18px; color: #333; padding:20px 0;}
.iframe { text-align: center;}
.iframe ul li{ display: inline-block; width: 250px; height: 600px; padding:20px; margin:20px 10px; list-style: none; box-shadow: 0 0 3px 3px #e3e3e3; overflow: auto;}

/*左侧导航菜单    home*/
.sideMenu{ border:1px solid #ddd; }
.sideMenu h3{ height:32px; line-height:32px; padding-left:10px; border-top:1px solid #e3e3e3; background:#f4f4f4; cursor:pointer; font-family: 'Microsoft YaHei'; font-size: 14px; color: #333;
}
.sideMenu ul .nLi.on .sub{ display: block;}
.sideMenu ul .nLi .sub{ padding:8px 0; color:#999; display:none; }
.sideMenu ul .nLi .sub li{ height: auto; line-height: 30px; list-style: none;}
.sideMenu ul .nLi .sub li a{ display: block; width: 100%; height: 100%; font-family: 'Microsoft YaHei'; font-size: 14px; color: #333; text-decoration: none; padding:0 25px; box-sizing:border-box;}
.sideMenu ul .nLi .sub li a:hover{ background:#eee;}
.sideMenu ul .nLi h3 span{ display:inline-block;margin-left:10px;margin-right: 30px;}
.sideMenu ul .nLi .sub li p{ display:inline-block;margin-left:10px;margin-right: 30px;}
.sideMenu ul .nLi .sub li table tr td{ display:inline-block;margin-left:10px;color: #999;}
/*左侧导航菜单    end*/
</style>";
        $output[] = "\n";
        $output[] = "<script type=\"text/javascript\">
    $(function(){
        $(\".sideMenu .nLi h3\").click(function(){
            if($(this).parent(\".nLi\").hasClass(\"on\")){
                $(this).next(\".sub\").slideUp(300,function(){
                    $(this).parent(\".nLi\").removeClass(\"on\")
                });
            }else{
                $(this).next(\".sub\").slideDown(300,function(){
                    $(this).parent(\".nLi\").addClass(\"on\")
                });
            }
        })
    })
</script>";
        $output[] = "\n";

        if ($this->timeList) {
            $total_num = count($this->timeList);

            $output[] = "<h2>耗时查看-TimeList total count is " . count($this->timeList) . ", log time is " . $this->_intervalTime($this->timeList[0][1], $this->timeList[$total_num - 1][1]) . "</h2>";
            $output[] = " <div class=\"sideMenu\"><ul>";

            $lasttime = '0.00';

            foreach ($this->timeList as $info) {
                $lasttime2 = $info[1];
                $output[] = ' <li class="nLi">';
                $diff = $this->_intervalTime($lasttime, $lasttime2);
                if ($lasttime != '0.00' && $diff > '1000') {
                    $output[] = '<h3 style="background: #f9e2e6">';
                } else {
                    $output[] = '<h3>';
                }
                $output[] = '<span>标签：' . $info[0]
                    . '</span><span>花费：' . $diff . 'ms</span>'
                    . '</h3><ul class="sub">
                <li><p> 时间段：' . $lasttime . '~' . $lasttime2 . '</p></li>
            </ul></li> ';
                $lasttime = $lasttime2;
            }
            $output[] = '</ul>';
        }
        if ($this->infoList) {
            $total_num = count($this->infoList);
            $output[] = "<h2>日志查看-InfoList total count is " . count($this->infoList) . ", log time is " . $this->_intervalTime($this->infoList[0]['addTime'], $this->infoList[$total_num - 1]['addTime']) . "</h2>";
            $output[] = " <div class=\"sideMenu\"><ul>";

            foreach ($this->infoList as $info) {
                $output[] = ' <li class="nLi"><h3><span>标签：' . $info['label']
                    . '</span></h3><ul class="sub"><li><table> ';
                foreach ($info as $k => $v) {
                    $output[] = '<tr><td><p>' . $k . '&nbsp:&nbsp' . $v . '</p></td></tr>';
                }
                $output[] = '</table></li></ul></li>';
            }
            $output[] = '</ul>';
        }
        return implode("\n", $output);
    }

}

